import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image, ImageTk, ImageColor
import numpy as np
import os
import pandas as pd
from colorspacious import cspace_convert
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
import colorsys
import os
from openpyxl.styles import PatternFill, Font
from openpyxl.utils import get_column_letter

class ColorMatcherApp:
    def __init__(self, root):
        self.root = root
        self.root.title("颜色匹配工具")
        self.root.geometry("1200x700")  # 增加窗口宽度以适应内容
        self.root.resizable(True, True)

        # 初始化变量
        self.image_path = None
        self.palette_folder = None
        self.excel_path = None
        self.delta_e_threshold = tk.StringVar(value="20")
        self.processing = False
        self.palette_colors = {}  # 缓存预处理后的颜色数据 {file_path: {color_data}}
        self.current_palette_folder = None  # 用于检测色库是否变化
        self.thread_pool = ThreadPoolExecutor(max_workers=4)  # 多线程池
        self.match_mode = tk.StringVar(value="image")  # 匹配模式: image/rgb
        self.rgb_values = [tk.StringVar(value="0"), tk.StringVar(value="0"), tk.StringVar(value="0")]  # R, G, B值
        self.target_color = None  # 目标颜色
        self.target_hex = None    # 目标颜色的十六进制值
        self.distance_mode = tk.StringVar(value="delta_e")  # 距离计算模式: delta_e/rgb_euclidean
        self.default_thresholds = {"delta_e": "20", "rgb_euclidean": "50"}  # 不同模式的默认阈值

        # 创建界面组件
        self.create_widgets()

    def create_widgets(self):
        # 左侧面板
        left_frame = tk.Frame(self.root, width=300, height=600)
        left_frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=10, pady=10)

        # 匹配模式选择
        tk.Label(left_frame, text="匹配模式:").pack(pady=(10, 0))
        mode_frame = tk.Frame(left_frame)
        mode_frame.pack(fill=tk.X, padx=20)
        tk.Radiobutton(mode_frame, text="图片选择", variable=self.match_mode, value="image", command=self.toggle_match_mode).pack(side=tk.LEFT)
        tk.Radiobutton(mode_frame, text="RGB输入", variable=self.match_mode, value="rgb", command=self.toggle_match_mode).pack(side=tk.RIGHT)

        # 图片预览区域 - 调整大小为约100px高度
        self.image_label = tk.Label(left_frame, text="请上传图片", bg="#f0f0f0", width=30, height=6)  # height=6约等于100px
        self.image_label.pack(pady=10)

        # RGB输入区域
        self.rgb_frame = tk.Frame(left_frame)
        rgb_labels = ["R:", "G:", "B:"]
        self.rgb_entries = []
        for i in range(3):
            tk.Label(self.rgb_frame, text=rgb_labels[i]).grid(row=0, column=i*2, padx=2)
            entry = tk.Entry(self.rgb_frame, textvariable=self.rgb_values[i], width=5)
            entry.grid(row=0, column=i*2+1, padx=2)
            self.rgb_entries.append(entry)
        self.rgb_frame.pack(pady=10)
        self.rgb_frame.pack_forget()  # 默认隐藏

        # 上传图片按钮
        self.upload_btn = tk.Button(left_frame, text="上传图片", command=self.upload_image)
        self.upload_btn.pack(pady=5)

        # 选择色库文件夹按钮 - 创建为实例变量
        self.select_palette_folder_btn = tk.Button(left_frame, text="选择色库文件夹", command=self.select_palette_folder)
        self.select_palette_folder_btn.pack(pady=5)

        # 选择Excel文件按钮
        tk.Button(left_frame, text="选择Excel文件", command=self.select_excel_file).pack(pady=5)

        # 距离计算模式选择
        tk.Label(left_frame, text="距离计算方式:").pack(pady=(10, 0))
        distance_frame = tk.Frame(left_frame)
        distance_frame.pack(fill=tk.X, padx=20)
        tk.Radiobutton(distance_frame, text="Delta E", variable=self.distance_mode, value="delta_e", command=self.update_threshold_default).pack(side=tk.LEFT)
        tk.Radiobutton(distance_frame, text="RGB欧氏距离", variable=self.distance_mode, value="rgb_euclidean", command=self.update_threshold_default).pack(side=tk.RIGHT)

        # Delta E输入框
        tk.Label(left_frame, text="匹配阈值:").pack()
        tk.Entry(left_frame, textvariable=self.delta_e_threshold).pack(pady=5)

        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(left_frame, variable=self.progress_var, length=200, mode='determinate')
        self.progress_bar.pack(pady=10)
        self.progress_label = tk.Label(left_frame, text="进度: 0%")
        self.progress_label.pack()

        # 匹配按钮
        tk.Button(left_frame, text="开始匹配", command=self.start_match_thread).pack(pady=10)
        
        # 添加导出Excel按钮
        self.export_btn = tk.Button(left_frame, text="导出结果到Excel", command=self.export_results_to_excel)
        self.export_btn.pack(pady=5)
        
        # 右侧面板 - 三列布局（增加宽度）
        right_frame = tk.Frame(self.root, width=600, height=600)  # 增加右侧面板宽度
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, padx=10, pady=10, expand=True)

        # 创建带滚动条的Canvas
        self.canvas = tk.Canvas(right_frame)
        scrollbar = tk.Scrollbar(right_frame, orient="vertical", command=self.canvas.yview)
        self.scrollable_frame = tk.Frame(self.canvas)

        # 配置滚动区域
        self.scrollable_frame.bind(
            "<Configure>",
            lambda e: self.canvas.configure(
                scrollregion=self.canvas.bbox("all")
            )
        )

        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
        self.canvas.configure(yscrollcommand=scrollbar.set)

        # 创建三列（调整列宽）
        self.column1 = tk.Frame(self.scrollable_frame, width=100, padx=5)  # 目标颜色列
        self.column2 = tk.Frame(self.scrollable_frame, width=100, padx=5)  # 匹配颜色列
        self.column3 = tk.Frame(self.scrollable_frame, padx=5)             # 信息列（更宽）

        # 设置列标题
        tk.Label(self.column1, text="目标颜色", font=('SimHei', 10, 'bold')).pack(pady=5)
        tk.Label(self.column2, text="匹配颜色", font=('SimHei', 10, 'bold')).pack(pady=5)
        tk.Label(self.column3, text="颜色信息", font=('SimHei', 10, 'bold')).pack(pady=5)

        # 排列三列
        self.column1.pack(side=tk.LEFT, fill=tk.Y)
        self.column2.pack(side=tk.LEFT, fill=tk.Y)
        self.column3.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # 放置Canvas和滚动条
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 底部版权信息
        footer_frame = tk.Frame(root)
        footer_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=10)
        tk.Label(
            footer_frame,
            text="Techlink-数字化中心-李辉能 v1.1",
            font=('SimHei', 9),
            fg='#666666'
        ).pack(anchor=tk.CENTER)

        # 初始切换匹配模式
        self.toggle_match_mode()

    def update_threshold_default(self):
        """根据选择的距离模式更新阈值默认值"""
        self.delta_e_threshold.set(self.default_thresholds[self.distance_mode.get()])

    def toggle_match_mode(self):
        """切换匹配模式时显示/隐藏相应控件"""
        if self.match_mode.get() == "image":
            self.image_label.pack(pady=10)
            self.upload_btn.pack(pady=5, before=self.select_palette_folder_btn)
            self.rgb_frame.pack_forget()
        else:
            self.image_label.pack_forget()
            self.upload_btn.pack_forget()
            self.rgb_frame.pack(pady=10, before=self.select_palette_folder_btn)

    def upload_image(self):
        self.image_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg")])
        if self.image_path:
            self.display_image()
            messagebox.showinfo("成功", "图片上传成功！")

    def display_image(self):
        try:
            img = Image.open(self.image_path)
            # 调整图片大小以适应显示区域
            img.thumbnail((250, 250))
            photo = ImageTk.PhotoImage(img)
            self.image_label.config(image=photo)
            self.image_label.image = photo  # 保持引用
        except Exception as e:
            messagebox.showerror("错误", f"无法显示图片: {str(e)}")

    def select_palette_folder(self):
        new_folder = filedialog.askdirectory()
        if new_folder and new_folder != self.palette_folder:
            self.palette_folder = new_folder
            self.excel_path = None  # 重置Excel路径
            self.palette_colors = {}  # 重置缓存
            self.current_palette_folder = None
            # 选择文件夹后自动预处理并生成Excel
            threading.Thread(target=self.preprocess_and_generate_excel, daemon=True).start()

    def select_excel_file(self):
        # 设置默认路径和文件名
        initial_dir = os.getcwd()
        initial_file = 'color_palette.xlsx'
        default_path = os.path.join(initial_dir, initial_file)

        # 检查生成的Excel是否存在
        if hasattr(self, 'generated_excel_path') and os.path.exists(self.generated_excel_path):
            initial_dir = os.path.dirname(self.generated_excel_path)
            initial_file = os.path.basename(self.generated_excel_path)

        self.excel_path = filedialog.askopenfilename(
            filetypes=[("Excel files", "*.xlsx")],
            initialdir=initial_dir,
            initialfile=initial_file
        )
        if self.excel_path:
            self.palette_folder = None  # 重置文件夹路径
            self.load_colors_from_excel()
            messagebox.showinfo("成功", "Excel文件加载成功！")

    def load_colors_from_excel(self):
        try:
            df = pd.read_excel(self.excel_path)
            self.palette_colors = {}
            for _, row in df.iterrows():
                # 解析RGB值
                rgb_str = row['RGB'].strip('()').split(', ')
                rgb = (int(rgb_str[0]), int(rgb_str[1]), int(rgb_str[2]))
                self.palette_colors[row['文件路径']] = {
                    'rgb': rgb,
                    'hex': row['十六进制'],
                    'hsl': tuple(map(float, row['HSL'].strip('()').split(', '))),
                    'cmyk': tuple(map(float, row['CMYK'].strip('()').split(', '))),
                    'filename': row['文件名'],
                    'file_path': row['文件路径']  # 添加file_path键
                }
            self.current_palette_folder = f"excel:{self.excel_path}"
        except Exception as e:
            messagebox.showerror("错误", f"加载Excel失败: {str(e)}")

    def get_pixel_color(self, image_path):
        try:
            img = Image.open(image_path)
            img = img.convert("RGB")
            return img.getpixel((10, 10))
        except Exception as e:
            print(f"读取图片错误 {image_path}: {e}")
            return None

    def validate_rgb_input(self):
        """验证RGB输入是否有效"""
        try:
            r = int(self.rgb_values[0].get())
            g = int(self.rgb_values[1].get())
            b = int(self.rgb_values[2].get())
            if 0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255:
                return (r, g, b)
            else:
                messagebox.showerror("错误", "RGB值必须在0-255之间！")
                return None
        except ValueError:
            messagebox.showerror("错误", "RGB值必须是整数！")
            return None

    def rgb_to_hsl(self, rgb):
        r, g, b = [x/255.0 for x in rgb]
        h, l, s = colorsys.rgb_to_hls(r, g, b)
        return (round(h*360, 2), round(s*100, 2), round(l*100, 2))

    def rgb_to_cmyk(self, rgb):
        r, g, b = [x/255.0 for x in rgb]
        k = 1 - max(r, g, b)
        if k == 1:  # 黑色
            return (0, 0, 0, 100)
        c = (1 - r - k) / (1 - k)
        m = (1 - g - k) / (1 - k)
        y = (1 - b - k) / (1 - k)
        return (round(c*100, 2), round(m*100, 2), round(y*100, 2), round(k*100, 2))

    def rgb_to_hex(self, rgb):
        return "#%02x%02x%02x" % rgb

    def calculate_rgb_euclidean(self, color1, color2):
        """计算RGB颜色空间中的欧氏距离"""
        return np.sqrt(np.sum((np.array(color1) - np.array(color2)) ** 2))

    def calculate_delta_e(self, color1, color2):
        # 将RGB转换为LAB颜色空间
        lab1 = cspace_convert(color1, "sRGB255", "CIELab")
        lab2 = cspace_convert(color2, "sRGB255", "CIELab")
        return np.sqrt(np.sum((lab1 - lab2) ** 2))

    def start_match_thread(self):
        if self.processing:
            messagebox.showwarning("提示", "正在处理中，请稍候...")
            return

        if not self.palette_folder and not self.excel_path:
            messagebox.showerror("错误", "请先选择色库文件夹或Excel文件！")
            return

        # 获取目标颜色
        target_color = None
        if self.match_mode.get() == "image":
            if not self.image_path:
                messagebox.showerror("错误", "请先上传图片！")
                return
            target_color = self.get_pixel_color(self.image_path)
            if not target_color:
                messagebox.showerror("错误", "无法获取目标图片的颜色！")
                return
        else:
            target_color = self.validate_rgb_input()
            if not target_color:
                return

        try:
            threshold = float(self.delta_e_threshold.get())
        except ValueError:
            messagebox.showerror("错误", "Delta E阈值必须是数字！")
            return

        # 保存目标颜色
        self.target_color = target_color
        self.target_hex = self.rgb_to_hex(target_color)

        # 启动新线程进行匹配
        self.processing = True
        self.progress_var.set(0)
        self.progress_label.config(text="匹配中: 0%")
        threading.Thread(target=self.match_colors, args=(target_color, threshold), daemon=True).start()

    def preprocess_and_generate_excel(self):
        self.processing = True
        self.progress_var.set(0)
        self.progress_label.config(text="预处理中: 0%")
        success = self.preprocess_palette_images()
        self.processing = False
        if success:
            self.save_palette_to_excel()
            messagebox.showinfo("成功", f"已生成Excel文件到项目根目录: color_palette.xlsx")
        else:
            messagebox.showinfo("提示", "色库文件夹中没有PNG图片！")

    def preprocess_palette_images(self):
        """批量预处理所有色库图片的颜色值，包括子目录"""
        if self.palette_folder == self.current_palette_folder and self.palette_colors:
            return True  # 已预处理且色库未变化

        self.palette_colors = {}
        png_files = []

        # 遍历所有子目录查找PNG文件
        for root, dirs, files in os.walk(self.palette_folder):
            for file in files:
                if file.lower().endswith(".png"):
                    png_files.append(os.path.join(root, file))

        total_files = len(png_files)
        if total_files == 0:
            return False

        # 使用多线程并行处理图片
        futures = {
            self.thread_pool.submit(self.process_single_image, file_path): file_path
            for file_path in png_files
        }

        for i, future in enumerate(as_completed(futures)):
            file_path = futures[future]
            try:
                color_data = future.result()
                if color_data:
                    self.palette_colors[file_path] = color_data
            except Exception as e:
                print(f"处理{file_path}出错: {e}")

            # 更新进度
            progress = (i + 1) / total_files * 100
            self.root.after(0, lambda p=progress: self.update_progress(p, "预处理中"))

        self.current_palette_folder = self.palette_folder
        return True

    def process_single_image(self, file_path):
        """单张图片处理函数（供多线程调用）"""
        rgb = self.get_pixel_color(file_path)
        if not rgb:
            return None

        # 转换为多种颜色格式
        hex_color = self.rgb_to_hex(rgb)
        hsl = self.rgb_to_hsl(rgb)
        cmyk = self.rgb_to_cmyk(rgb)
        filename = os.path.basename(file_path)

        return {
            'rgb': rgb,
            'hex': hex_color,
            'hsl': hsl,
            'cmyk': cmyk,
            'filename': filename,
            'file_path': file_path
        }

    def save_palette_to_excel(self):
        """将预处理后的颜色数据保存到Excel文件"""
        if not self.palette_colors:
            return

        # 准备数据
        data = []
        for color_info in self.palette_colors.values():
            data.append({
                '文件名': color_info['filename'],
                '文件路径': color_info['file_path'],
                'RGB': color_info['rgb'],
                '十六进制': color_info['hex'],
                'HSL': color_info['hsl'],
                'CMYK': color_info['cmyk']
            })

        # 创建DataFrame并保存到Excel
        df = pd.DataFrame(data)
        excel_path = os.path.join(os.getcwd(), 'color_palette.xlsx')
        try:
            df.to_excel(excel_path, index=False)
            return True
        except Exception as e:
            messagebox.showerror("错误", f"生成Excel失败: {str(e)}")
            return False

    def match_colors(self, target_color, threshold):
        # 清空之前的结果
        self.root.after(0, self.clear_results)

        # 检查是否需要预处理
        if self.palette_folder and not self.preprocess_palette_images():
            self.root.after(0, lambda: messagebox.showinfo("提示", "色库文件夹中没有PNG图片！"))
            self.processing = False
            return

        # 使用预处理好的颜色数据进行匹配
        palette_items = list(self.palette_colors.items())
        total_items = len(palette_items)
        results = []  # 存储匹配结果用于排序

        for i, (file_path, color_info) in enumerate(palette_items):
            if not self.processing:  # 检查是否需要取消
                break

            # 根据选择的距离模式计算距离
            if self.distance_mode.get() == "delta_e":
                distance = self.calculate_delta_e(target_color, color_info['rgb'])
            else:  # rgb_euclidean
                distance = self.calculate_rgb_euclidean(target_color, color_info['rgb'])

            if distance <= threshold:
                # 将结果添加到列表而非立即显示
                results.append((distance, color_info['filename'], color_info))

            # 更新进度
            progress = (i + 1) / total_items * 100
            self.root.after(0, lambda p=progress: self.update_progress(p, "匹配中"))

        # 按距离值从小到大排序结果
        results.sort(key=lambda x: x[0])
        
        # 保存匹配结果到实例变量
        self.matching_results = results
        
        # 显示排序后的结果
        for distance, filename, color_info in results:
            self.root.after(0, lambda fn=filename, ci=color_info, de=distance: 
                            self.update_result(fn, ci, de))
        
        self.processing = False
        self.root.after(0, lambda: self.progress_label.config(text="处理完成！"))

    def clear_results(self):
        """清空结果区域"""
        for widget in self.column1.winfo_children():
            if widget.winfo_class() != 'Label':  # 保留标题
                widget.destroy()
        for widget in self.column2.winfo_children():
            if widget.winfo_class() != 'Label':  # 保留标题
                widget.destroy()
        for widget in self.column3.winfo_children():
            if widget.winfo_class() != 'Label':  # 保留标题
                widget.destroy()

    def update_progress(self, value, stage="处理中"):
        self.progress_var.set(value)
        self.progress_label.config(text=f"{stage}: {int(value)}%")

    def update_result(self, filename, color_info, distance):
        """更新结果区域，添加三列布局的结果"""
        # 设置统一的行高度（像素单位）
        ROW_HEIGHT = 40
        
        # 第一列：目标颜色色块
        target_canvas = tk.Canvas(self.column1, width=80, height=ROW_HEIGHT, bg=self.target_hex, bd=1, relief=tk.SOLID)
        target_canvas.pack(fill=tk.X, pady=2)

        # 第二列：匹配颜色色块
        match_canvas = tk.Canvas(self.column2, width=80, height=ROW_HEIGHT, bg=color_info['hex'], bd=1, relief=tk.SOLID)
        match_canvas.pack(fill=tk.X, pady=2)

        # 第三列：颜色信息（水平排列，使用Entry组件实现可复制）
        info_frame = tk.Frame(self.column3, width=880, height=ROW_HEIGHT+6)
        info_frame.pack(fill=tk.X, pady=2, anchor=tk.W)
        info_frame.pack_propagate(False)  # 防止内部组件改变Frame大小

        # 水平排列所需信息，显示当前距离计算模式
        distance_label_text = "Delta E" if self.distance_mode.get() == "delta_e" else "RGB欧氏距离"
        
        # 创建可复制的Entry组件替代Label
        distance_text = f"{distance_label_text}: {distance:.2f}"
        entry = tk.Entry(info_frame, width=20, justify=tk.LEFT, bd=0, bg=info_frame.cget("bg"))
        entry.insert(0, distance_text)
        entry.configure(state='readonly')
        entry.pack(side=tk.LEFT, padx=5, pady=(5,5))

        rgb_text = f"RGB: {color_info['rgb']}"
        entry = tk.Entry(info_frame, width=22, justify=tk.LEFT, bd=0, bg=info_frame.cget("bg"))
        entry.insert(0, rgb_text)
        entry.configure(state='readonly')
        entry.pack(side=tk.LEFT, padx=5, pady=(5,5))

        filename_text = f"文件名: {filename}"
        # 如果文件名包含TPG则显示为蓝色
        text_color = 'blue' if 'TPG' in filename else 'black'
        entry = tk.Entry(info_frame, width=30, justify=tk.LEFT, bd=0, bg=info_frame.cget("bg"), fg=text_color)
        entry.insert(0, filename_text)
        entry.configure(state='readonly')
        # 绑定双击事件打开文件
        entry.bind("<Double-Button-1>", lambda e: self.open_file(color_info['file_path']))
        entry.pack(side=tk.LEFT, padx=5, pady=(5,5))
        hsl_text = f"HSL: {color_info['hsl']}"
        entry = tk.Entry(info_frame, width=35, justify=tk.LEFT, bd=0, bg=info_frame.cget("bg"))
        entry.insert(0, hsl_text)
        entry.configure(state='readonly')
        entry.pack(side=tk.LEFT, padx=5, pady=(5,5))

        # 移除错误的height设置，使用框架高度控制整体行高
        # for widget in info_frame.winfo_children():
        #     widget.configure(height=30, anchor=tk.CENTER)

    def open_file(self, file_path):
        """双击打开文件"""
        if not file_path or not os.path.exists(file_path):
            messagebox.showerror("错误", "文件路径不存在或无效")
            return
            
        try:
            os.startfile(file_path)
        except Exception as e:
            messagebox.showerror("错误", f"打开文件失败: {str(e)}")

    def export_results_to_excel(self):
        """将匹配结果导出到Excel文件，包含颜色色块"""
        if not hasattr(self, 'matching_results') or not self.matching_results:
            messagebox.showinfo("提示", "没有可导出的匹配结果！")
            return

        # 获取保存路径
        file_path = filedialog.asksaveasfilename(
            defaultextension=".xlsx",
            filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
            initialfile="matching_results.xlsx"
        )

        if not file_path:
            return  # 用户取消保存

        try:
            # 准备导出数据，添加色块列
            data = []
            distance_label = "Delta E" if self.distance_mode.get() == "delta_e" else "RGB欧氏距离"
            target_rgb = self.target_color
            target_hex = self.target_hex
            target_hsl = self.rgb_to_hsl(target_rgb)

            for distance, filename, color_info in self.matching_results:
                data.append({
                    '目标颜色色块': '',  # 占位列，用于填充颜色
                    '匹配颜色色块': '',  # 占位列，用于填充颜色
                    '目标颜色(RGB)': target_rgb,
                    '目标颜色(HEX)': target_hex,
                    '目标颜色(HSL)': target_hsl,
                    distance_label: round(distance, 2),
                    '匹配颜色(RGB)': color_info['rgb'],
                    '匹配颜色(HEX)': color_info['hex'],
                    '匹配颜色(HSL)': color_info['hsl'],
                    '文件名': filename,
                    '文件路径': color_info['file_path']
                })

            # 保存到Excel
            df = pd.DataFrame(data)
            # 使用openpyxl引擎保存，以便后续修改格式
            with pd.ExcelWriter(file_path, engine='openpyxl') as writer:
                df.to_excel(writer, index=False, sheet_name='匹配结果')
                
                # 获取工作表对象
                workbook = writer.book
                worksheet = writer.sheets['匹配结果']
                
                # 设置列宽
                worksheet.column_dimensions['A'].width = 15  # 目标颜色色块列
                worksheet.column_dimensions['B'].width = 15  # 匹配颜色色块列
                
                # 设置颜色色块
                # 设置列宽
                worksheet.column_dimensions['A'].width = 15  # 目标颜色色块列
                worksheet.column_dimensions['B'].width = 15  # 匹配颜色色块列
                worksheet.column_dimensions[get_column_letter(10)].width = 25  # 文件名列
                
                # 设置颜色色块和超链接
                for row in range(2, len(data) + 2):  # 从第二行开始（跳过表头）
                    current_data = data[row - 2]  # 数据索引校正
                    
                    # 设置目标颜色色块（A列）
                    target_cell = worksheet.cell(row=row, column=1)
                    target_cell.fill = PatternFill(start_color=target_hex[1:], end_color=target_hex[1:], fill_type='solid')
                    
                    # 设置匹配颜色色块（B列）
                    match_color_hex = current_data['匹配颜色(HEX)'][1:]
                    match_cell = worksheet.cell(row=row, column=2)
                    match_cell.fill = PatternFill(start_color=match_color_hex, end_color=match_color_hex, fill_type='solid')
                    
                    # 设置文件名超链接（J列）
                    file_name_cell = worksheet.cell(row=row, column=10)
                    file_path = current_data['文件路径'].replace('\\', '/')  # 转换为斜杠路径
                    file_name_cell.hyperlink = f'file:///{file_path}'
                    file_name_cell.value = current_data['文件名']
                    file_name_cell.font = Font(color='0000FF', underline='single')

            messagebox.showinfo("成功", f"匹配结果已导出到:\n{file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"导出Excel失败: {str(e)}")

if __name__ == "__main__":
    root = tk.Tk()
    app = ColorMatcherApp(root)
    root.mainloop()